﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Threading.Tasks;
using System.Windows;
using System.Diagnostics.Contracts;

namespace Infrastructure
{
    /// <summary>
    /// Extension methods for DataGrid
    /// </summary>
    public static class DataGridHelper
    {
        /// <summary>
        /// Gets the visual child of an element
        /// </summary>
        /// <typeparam name="T">Expected type</typeparam>
        /// <param name="parent">The parent of the expected element</param>
        /// <returns>A visual child</returns>
        public static T GetVisualChild<T>(Visual parent) where T : Visual
        {
            Contract.Requires(parent != null);

            T child = default(T);
            int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
            for (int i = 0; i < numVisuals; i++)
            {
                Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
                child = v as T;
                if (child == null)
                {
                    child = GetVisualChild<T>(v);
                }
                if (child != null)
                {
                    break;
                }
            }
            return child;
        }

        /// <summary>
        /// Gets the visual parent of an element
        /// </summary>
        /// <typeparam name="T">Expected type</typeparam>
        /// <param name="element">The child of the expected element</param>
        /// <returns>A visual parent</returns>
        public static T FindVisualParent<T>(UIElement element) where T : UIElement
        {
            Contract.Requires(element != null);

            UIElement parent = element;
            while (parent != null)
            {
                T correctlyTyped = parent as T;
                if (correctlyTyped != null)
                {
                    return correctlyTyped;
                }

                parent = VisualTreeHelper.GetParent(parent) as UIElement;
            }
            return null;
        }



        /// <summary>
        /// Gets the specified cell of the DataGrid
        /// </summary>
        /// <param name="grid">The DataGrid instance</param>
        /// <param name="row">The row of the cell</param>
        /// <param name="column">The column index of the cell</param>
        /// <returns>A cell of the DataGrid</returns>
        public static DataGridCell GetCell(this DataGrid grid, DataGridRow row, int column)
        {
            Contract.Requires(grid != null);
            Contract.Requires(row != null);
            Contract.Requires(column >= 0 && column < grid.Columns.Count);

            if (row != null)
            {
                DataGridCellsPresenter presenter = GetVisualChild<DataGridCellsPresenter>(row);

                if (presenter == null)
                {
                    grid.ScrollIntoView(row, grid.Columns[column]);
                    presenter = GetVisualChild<DataGridCellsPresenter>(row);
                }

                DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);

                return cell;
            }
            return null;
        }

        /// <summary>
        /// Gets the specified cell of the DataGrid
        /// </summary>
        /// <param name="grid">The DataGrid instance</param>
        /// <param name="row">The row index of the cell</param>
        /// <param name="column">The column index of the cell</param>
        /// <returns>A cell of the DataGrid</returns>
        public static DataGridCell GetCell(this DataGrid grid, int row, int column)
        {
            Contract.Requires(grid != null);
            Contract.Requires(column >= 0 && column < grid.Columns.Count);
            Contract.Requires(row >= 0 && row < grid.Items.Count);

            DataGridRow rowContainer = grid.GetRow(row);
            return grid.GetCell(rowContainer, column);
        }

        /// <summary>
        /// Gets the specified row of the DataGrid
        /// </summary>
        /// <param name="grid">The DataGrid instance</param>
        /// <param name="index">The index of the row</param>
        /// <returns>A row of the DataGrid</returns>
        public static DataGridRow GetRow(this DataGrid grid, int index)
        {
            Contract.Requires(grid != null);
            Contract.Requires(index >= 0 && index < grid.Items.Count);

            DataGridRow row = (DataGridRow)grid.ItemContainerGenerator.ContainerFromIndex(index);
            if (row == null)
            {
                // May be virtualized, bring into view and try again.
                grid.UpdateLayout();
                grid.ScrollIntoView(grid.Items[index]);
                row = (DataGridRow)grid.ItemContainerGenerator.ContainerFromIndex(index);
            }
            return row;
        }

        /// <summary>
        /// Gets the selected row of the DataGrid
        /// </summary>
        /// <param name="grid">The DataGrid instance</param>
        /// <returns></returns>
        public static DataGridRow GetSelectedRow(this DataGrid grid)
        {
            Contract.Requires(grid != null);

            return (DataGridRow)grid.ItemContainerGenerator.ContainerFromItem(grid.SelectedItem);
        }

        /// <summary>
        /// Gets the row of the DataGrid.
        /// </summary>
        /// <param name="grid">The grid.</param>
        /// <param name="cell">The cell.</param>
        /// <returns></returns>
        public static DataGridRow GetRow(this DataGrid grid, DataGridCell cell)
        {
            Contract.Requires(grid != null);
            Contract.Requires(cell != null);

            return FindVisualParent<DataGridRow>(cell);
        }

        /// <summary>
        /// Sets the cell in edit mode. Cell is selected, content editable.
        /// </summary>
        /// <param name="grid">The grid.</param>
        /// <param name="cell">The cell.</param>
        public static void EditCell(this DataGrid grid, DataGridCell cell)
        {
            Contract.Requires(grid != null);
            Contract.Requires(cell != null);

            // Focus cell
            if (!cell.IsFocused)
            {
                cell.Focus();
            }

            // Select cell or row for editing
            if (grid.SelectionUnit != DataGridSelectionUnit.FullRow)
            {
                if (!cell.IsSelected)
                {
                    cell.IsSelected = true;
                }
            }
            else
            {
                var row = grid.GetRow(cell);
                if (row != null && !row.IsSelected)
                {
                    row.IsSelected = true;
                }
            }
        }
    }
}
